#pragma once
#include "Singleton.h"

#include <thread>
#include <vector>
#include <queue>
#include <mutex>
#include <condition_variable>
#include <functional>
#include <future>
#include <iostream>
#include <atomic>
#include <stdexcept>
#include <type_traits>
#include <tuple>

enum class TaskPriority : int
{
  High = 0,
  Medium = 5,
  Low = 10,
};

class ThreadPool : public Singleton<ThreadPool>
{
  friend class Singleton<ThreadPool>;

public:
  using Task = std::function<void()>;

  void init(size_t maxQueueSize = 1000)
  {
    std::unique_lock<std::mutex> lock(poolMutex_);
    if (initialized_)
      return;

    maxQueueSize_ = maxQueueSize;
    isRunning_ = true;

    size_t threadCount = std::max(1u, std::thread::hardware_concurrency());

    for (size_t i = 0; i < threadCount; ++i)
    {
      workers_.emplace_back([this]()
                            {
                while (true) {
                    Task task = getTask();
                    if (!task) break; // 线程池关闭信号

                    try {
                        task();
                    } catch (const std::exception& e) {
                        std::cerr << "[ThreadPool] Task exception: " << e.what() << std::endl;
                    } catch (...) {
                        std::cerr << "[ThreadPool] Task unknown exception" << std::endl;
                    }
                } });
    }

    initialized_ = true;
  }

  template <typename F, typename... Args>
  void enqueue(TaskPriority priority, F &&f, Args &&...args)
  {
    enqueueImpl(priority, std::forward<F>(f), std::forward<Args>(args)...);
  }

  void shutdown()
  {
    {
      std::unique_lock<std::mutex> lock(queueMutex_);
      isRunning_ = false;
    }
    condition_.notify_all();

    for (auto &t : workers_)
    {
      if (t.joinable())
        t.join();
    }
    workers_.clear();

    initialized_ = false;

    clearQueues();
  }

  ~ThreadPool()
  {
    shutdown();
  }

  // 状态查询接口
  bool isInitialized() const
  {
    return initialized_ && isRunning_;
  }

  size_t getQueueSize() const
  {
    std::lock_guard<std::mutex> lock(queueMutex_);
    return highPriorityQueue_.size() + mediumPriorityQueue_.size() + lowPriorityQueue_.size();
  }

  size_t getMaxQueueSize() const
  {
    return maxQueueSize_;
  }

  bool canAcceptTask() const
  {
    return isInitialized() && (getQueueSize() < maxQueueSize_);
  }

private:
  ThreadPool() : isRunning_(false), initialized_(false), maxQueueSize_(1000) {}

  // 判断最后一个参数是否是回调
  template <typename F, typename Last>
  struct is_callback
  {
    using ReturnType = typename std::invoke_result_t<F>;
    static constexpr bool value = std::is_invocable_v<Last, ReturnType>;
  };

  // 不带回调版本
  template <typename F, typename... Args>
  std::enable_if_t<
      !(sizeof...(Args) > 0 &&
        is_callback<F, std::tuple_element_t<sizeof...(Args) - 1, std::tuple<Args...>>>::value),
      void>
  enqueueImpl(TaskPriority priority, F &&f, Args &&...args)
  {
    using ReturnType = std::invoke_result_t<F, Args...>;

    auto taskPtr = std::make_shared<std::packaged_task<ReturnType()>>(
        std::bind(std::forward<F>(f), std::forward<Args>(args)...));

    Task wrappedTask = [taskPtr]()
    {
      try
      {
        (*taskPtr)();
      }
      catch (const std::exception &e)
      {
        std::cerr << "[ThreadPool] Task exception: " << e.what() << std::endl;
      }
      catch (...)
      {
        std::cerr << "[ThreadPool] Task unknown exception" << std::endl;
      }
    };

    enqueueTask(priority, std::move(wrappedTask));
  }

  // 带回调版本
  template <typename F, typename Callback, typename... Args>
  std::enable_if_t<
      is_callback<F, Callback>::value,
      void>
  enqueueImpl(TaskPriority priority, F &&f, Callback &&cb, Args &&...args)
  {
    using ReturnType = std::invoke_result_t<F, Args...>;

    auto taskPtr = std::make_shared<std::packaged_task<ReturnType()>>(
        std::bind(std::forward<F>(f), std::forward<Args>(args)...));

    auto callbackPtr = std::make_shared<std::decay_t<Callback>>(std::forward<Callback>(cb));

    Task wrappedTask = [taskPtr, callbackPtr]()
    {
      try
      {
        (*taskPtr)();
        if (callbackPtr)
        {
          (*callbackPtr)(taskPtr->get_future().get());
        }
      }
      catch (const std::exception &e)
      {
        std::cerr << "[ThreadPool] Task exception: " << e.what() << std::endl;
      }
      catch (...)
      {
        std::cerr << "[ThreadPool] Task unknown exception" << std::endl;
      }
    };

    enqueueTask(priority, std::move(wrappedTask));
  }

  void enqueueTask(TaskPriority priority, Task &&task)
  {
    std::unique_lock<std::mutex> lock(queueMutex_);
    size_t currentQueueSize = highPriorityQueue_.size() + mediumPriorityQueue_.size() + lowPriorityQueue_.size();

    if (currentQueueSize >= maxQueueSize_)
    {
      throw std::runtime_error("Task queue full");
    }

    switch (priority)
    {
    case TaskPriority::High:
      highPriorityQueue_.emplace(std::move(task));
      break;
    case TaskPriority::Medium:
      mediumPriorityQueue_.emplace(std::move(task));
      break;
    case TaskPriority::Low:
    default:
      lowPriorityQueue_.emplace(std::move(task));
      break;
    }
    condition_.notify_one();
  }

  Task getTask()
  {
    std::unique_lock<std::mutex> lock(queueMutex_);
    condition_.wait(lock, [this]()
                    { return !isRunning_ || !highPriorityQueue_.empty() || !mediumPriorityQueue_.empty() || !lowPriorityQueue_.empty(); });

    if (!isRunning_ && highPriorityQueue_.empty() && mediumPriorityQueue_.empty() && lowPriorityQueue_.empty())
    {
      return nullptr;
    }

    if (!highPriorityQueue_.empty())
    {
      Task task = std::move(highPriorityQueue_.front());
      highPriorityQueue_.pop();
      return task;
    }
    if (!mediumPriorityQueue_.empty())
    {
      Task task = std::move(mediumPriorityQueue_.front());
      mediumPriorityQueue_.pop();
      return task;
    }
    if (!lowPriorityQueue_.empty())
    {
      Task task = std::move(lowPriorityQueue_.front());
      lowPriorityQueue_.pop();
      return task;
    }

    return nullptr;
  }

  void clearQueues()
  {
    while (!highPriorityQueue_.empty())
      highPriorityQueue_.pop();
    while (!mediumPriorityQueue_.empty())
      mediumPriorityQueue_.pop();
    while (!lowPriorityQueue_.empty())
      lowPriorityQueue_.pop();
  }

  std::vector<std::thread> workers_;

  std::queue<Task> highPriorityQueue_;
  std::queue<Task> mediumPriorityQueue_;
  std::queue<Task> lowPriorityQueue_;

  mutable std::mutex queueMutex_;
  std::condition_variable condition_;

  std::atomic<bool> isRunning_;
  bool initialized_;
  size_t maxQueueSize_;
  std::mutex poolMutex_;
};
